package org.ourgrid.broker.business.schedulerdata.executor;

import java.util.ArrayList;
import java.util.List;

import org.ourgrid.broker.business.dao.BrokerDAOFactory;
import org.ourgrid.broker.business.dao.WorkerDAO;
import org.ourgrid.broker.business.schedulerdata.SchedulerData;
import org.ourgrid.broker.response.ReportReplicaAccountingResponseTO;
import org.ourgrid.broker.response.to.GetOperationTO;
import org.ourgrid.broker.response.to.InitOperationTO;
import org.ourgrid.broker.response.to.PeerBalanceTO;
import org.ourgrid.broker.response.to.TransferProgressTO;
import org.ourgrid.common.internal.IResponseTO;
import org.ourgrid.common.spec.job.JobSpec;
import org.ourgrid.common.spec.worker.WorkerSpec;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

public class ReportReplicaAccountingExecutor extends AbstractSchedulerDataExecutor {
	
	
	/**
	 * <REPORT_ACCOUNTING peerID='String' workerID='String' state='String' initCPUTime='long'
	 *                    requestID='long' taskSequenceNumber='int' creationTime='long'
	 *                    latestPhase='String' sabotageCheck='String' jobID='int' requiredWorkers='int'
	 *         			  maxFails='int' maxReplicas='int'/>
	 *                    
	 *      <PHASES_DATA initBeginning='long' initEnd='long' remoteBeginning='long' remoteEnd='long'
	 *                   finalBeginning='long' finalEnd='long'>
	 *                   
	 *            1..n <INIT_OPERATION jobID='int' taskID='int' processID='int' requestID='long' 
	 *                                 workerID='String' localFilePath='String' remoteFilePath='String' 
	 *                                 transferDescription='String' initTime='long' endTime='long' fileSize='long'/>
	 *                                 
	 *            1..n <GET_OPERATION jobID='int' taskID='int' processID='int' requestID='long' 
	 *                                 workerID='String' localFilePath='String' remoteFilePath='String' 
	 *                                 transferDescription='String' initTime='long' endTime='long' fileSize='long'/>
	 *                    
	 *      </PHASES_DATA>              
	 * 
	 *      <PROCESS_RESULT_INFO exitValue='int' errorCause='String' executionErrorType='String'
	 *                           stderr='String' stdout='String'/>
	 *                           
	 * 1..n <PEER_BALANCE attribute='String' value='String' />
	 *                     
	 * 1..n	<TRANSFER_PROGRESS handleID='long' localFileName='String' destinationFileName='String' 
	 *                         fileSize='long' description='String'
	 *                         id='String' newStatus='String' amountWritten='long' progress='double'
	 *                         transferRate='double' outgoing='boolean'/>
	 * 
	 * </REPORT_ACCOUNTING>
	 */

	public List<IResponseTO> execute(SchedulerData command) {
		List<IResponseTO> responses = new ArrayList<IResponseTO>();
		
		Document logDocFile = super.getXMl(command.getXml());
		Element element = logDocFile.getDocumentElement();
		
		//acc
		String peerID = element.getAttribute("peerID");
		long requestID = Long.valueOf(element.getAttribute("requestID"));
		int jobID = Integer.valueOf(element.getAttribute("jobID"));
		int requiredWorkers = Integer.valueOf(element.getAttribute("requiredWorkers"));
		int maxFails = Integer.valueOf(element.getAttribute("maxFails"));
		int maxReplicas = Integer.valueOf(element.getAttribute("maxReplicas"));
		String workerID = element.getAttribute("workerID");
		long creationTime = Long.valueOf(element.getAttribute("creationTime"));
		String latestPhase = element.getAttribute("latestPhase");
		String sabotageCheck = element.getAttribute("sabotageCheck");
		int taskSequenceNumber = Integer.valueOf(element.getAttribute("taskSequenceNumber"));
		int processSequenceNumber = Integer.valueOf(element.getAttribute("processSequenceNumber"));
		String state = element.getAttribute("state");
		
		WorkerDAO workerDAO = BrokerDAOFactory.getInstance().getWorkerDAO();
		
		String workerAddress = getAddress(workerID);
		String workerPK = workerDAO.getWorkerPublicKey(workerAddress);
		
		//phase data
		NamedNodeMap attributes = element.getElementsByTagName("PHASES_DATA").item(0).getAttributes();
		
		long initBeginning = Long.valueOf(attributes.getNamedItem("initBeginning").getNodeValue());
		long initEnd = Long.valueOf(attributes.getNamedItem("initEnd").getNodeValue());
		long remoteBeginning = Long.valueOf(attributes.getNamedItem("remoteBeginning").getNodeValue());
		long remoteEnd = Long.valueOf(attributes.getNamedItem("remoteEnd").getNodeValue());
		long finalBeginning = Long.valueOf(attributes.getNamedItem("finalBeginning").getNodeValue());
		long finalEnd = Long.valueOf(attributes.getNamedItem("finalEnd").getNodeValue());
		
		//result info
		Node resultNode = element.getElementsByTagName("PROCESS_RESULT_INFO").item(0);
		NamedNodeMap resultAttributes = resultNode.getAttributes();
		
		int exitValue = Integer.valueOf(resultAttributes.getNamedItem("exitValue").getNodeValue());
		String errorCause = resultAttributes.getNamedItem("errorCause").getNodeValue();
		String executionErrorType = resultAttributes.getNamedItem("executionErrorType").getNodeValue();
		
		String stderr = null;
		String stdout = null;
		
		for (int i = 0; i < resultNode.getChildNodes().getLength(); i++) {
			Node childNode = resultNode.getChildNodes().item(i);
			if (childNode.getNodeName().equals("stderr")) {
				stderr = childNode.getNodeValue();
			} else if (childNode.getNodeName().equals("stdout")) {
				stdout = childNode.getNodeValue();
			}
		}
		
		
		//fill responseTO
		ReportReplicaAccountingResponseTO to = new ReportReplicaAccountingResponseTO();
		to.setCreationTime(creationTime);
		to.setErrorCause(errorCause);
		to.setExecutionErrorType(executionErrorType);
		to.setExitValue(exitValue);
		to.setFinalBeginning(finalBeginning);
		to.setFinalEnd(finalEnd);
		to.setInitBeginning(initBeginning);
		to.setInitEnd(initEnd);
		to.setJobID(jobID);
		
		JobSpec jobSpec = BrokerDAOFactory.getInstance().getJobDAO().getJobSpec(jobID);
		to.setJobSpec(jobSpec);
		to.setLatestPhase(latestPhase);
		to.setMaxFails(maxFails);
		to.setMaxReplicas(maxReplicas);
		to.setPeerAddress(getAddress(peerID));
		to.setRemoteBeginning(remoteBeginning);
		to.setRemoteEnd(remoteEnd);
		to.setRequestID(requestID);
		to.setRequiredWorkers(requiredWorkers);
		to.setSabotageCheck(sabotageCheck);
		to.setState(state);
		to.setStderr(stderr);
		to.setStdout(stdout);
		to.setTaskSequenceNumber(taskSequenceNumber);
		to.setGridProcessSequenceNumber(processSequenceNumber);
		to.setWorkerID(workerID);
		to.setWorkerPK(workerPK);
		
		WorkerSpec workerSpec = workerDAO.getWorkerSpec(workerAddress);
		to.setWorkerSpec(workerSpec);
		
		to.setGetOperationsList(fillGetOperations(element));
		to.setInitOperationsList(fillInitOperations(element));
		to.setPeerBalancesList(fillPeerBalances(element));
		to.setTransferProgressList(fillTransferProgress(element));
		
		responses.add(to);
		
		return responses;
	}

	private List<TransferProgressTO> fillTransferProgress(Element element) {
		List<TransferProgressTO> transferProgressList = new ArrayList<TransferProgressTO>();
		
		int size = element.getElementsByTagName("TRANSFER_PROGRESS").getLength();
		
		NamedNodeMap tpAttributes = null;
		long handleID;
		String localFileName;
		long fileSize;
		String description;
		String id;
		String newStatus;
		long amountWritten;
		double progress;
		double transferRate;
		boolean outgoing;
		
		for (int i = 0; i < size; i++) {
			
			tpAttributes = element.getElementsByTagName("TRANSFER_PROGRESS").item(i).getAttributes();
			
			handleID = Long.valueOf(tpAttributes.getNamedItem("handleID").getNodeValue());
			localFileName = tpAttributes.getNamedItem("localFileName").getNodeValue();
			fileSize = Long.valueOf(tpAttributes.getNamedItem("fileSize").getNodeValue());
			description = tpAttributes.getNamedItem("description").getNodeValue();
			id = tpAttributes.getNamedItem("id").getNodeValue();
			newStatus = tpAttributes.getNamedItem("newStatus").getNodeValue();
			amountWritten = Long.valueOf(tpAttributes.getNamedItem("amountWritten").getNodeValue());
			progress = Double.valueOf(tpAttributes.getNamedItem("progress").getNodeValue());
			transferRate = Double.valueOf(tpAttributes.getNamedItem("transferRate").getNodeValue());
			outgoing = Boolean.valueOf(tpAttributes.getNamedItem("outgoing").getNodeValue());
			
			TransferProgressTO to = new TransferProgressTO();
			to.setAmountWritten(amountWritten);
			to.setDescription(description);
			to.setFileSize(fileSize);
			to.setHandleID(handleID);
			to.setId(id);
			to.setLocalFileName(localFileName);
			to.setNewStatus(newStatus);
			to.setOutgoing(outgoing);
			to.setProgress(progress);
			to.setTransferRate(transferRate);
			
			transferProgressList.add(to);
		}
		
		return transferProgressList;
	}

	private List<PeerBalanceTO> fillPeerBalances(Element element) {
		List<PeerBalanceTO> peerBalancesList = new ArrayList<PeerBalanceTO>();
		
		int size = element.getElementsByTagName("PEER_BALANCE").getLength();
		
		NamedNodeMap pbAttributes = null;
		String property = null;
		double value = 0;
		
		for (int i = 0; i < size; i++) {
			
			pbAttributes = element.getElementsByTagName("PEER_BALANCE").item(i).getAttributes();
			property = pbAttributes.getNamedItem("attribute").getNodeValue();
			value = Double.valueOf(pbAttributes.getNamedItem("value").getNodeValue());
			
			PeerBalanceTO to = new PeerBalanceTO();
			to.setProperty(property);
			to.setValue(value);
			
			peerBalancesList.add(to);
		}
		
		return peerBalancesList;
	}

	private List<GetOperationTO> fillGetOperations(Element element) {
		List<GetOperationTO> getOperationsList = new ArrayList<GetOperationTO>();
		
		int opSize = 0;
		
		opSize = element.getElementsByTagName("GET_OPERATION").getLength();
		
		NamedNodeMap opAttributes = null;
		int jobID;
		int taskID;
		int processID;
		long requestID2;
		String workerID2;
		String localFilePath;
		String remoteFilePath;
		String transferDescription;
		long initTime;
		long endTime;
		long fileSize;
		
		for (int i = 0; i < opSize; i++) {
			
			opAttributes = element.getElementsByTagName("GET_OPERATION").item(i).getAttributes();

			
			jobID = Integer.valueOf(opAttributes.getNamedItem("jobID").getNodeValue());
			taskID = Integer.valueOf(opAttributes.getNamedItem("taskID").getNodeValue());
			processID = Integer.valueOf(opAttributes.getNamedItem("processID").getNodeValue());
			requestID2 = Long.valueOf(opAttributes.getNamedItem("requestID").getNodeValue());
			workerID2 = opAttributes.getNamedItem("workerID").getNodeValue();
			localFilePath = opAttributes.getNamedItem("localFilePath").getNodeValue();
			remoteFilePath = opAttributes.getNamedItem("remoteFilePath").getNodeValue();
			transferDescription = opAttributes.getNamedItem("transferDescription").getNodeValue();
			initTime = Long.valueOf(opAttributes.getNamedItem("initTime").getNodeValue());
			endTime = Long.valueOf(opAttributes.getNamedItem("endTime").getNodeValue());
			fileSize = Long.valueOf(opAttributes.getNamedItem("fileSize").getNodeValue());
			
			GetOperationTO to = new GetOperationTO();
			to.setEndTime(endTime);
			to.setInitTime(initTime);
			to.setJobID(jobID);
			to.setLocalFilePath(localFilePath);
			to.setProcessID(processID);
			to.setRemoteFilePath(remoteFilePath);
			to.setRequestID2(requestID2);
			to.setTaskID(taskID);
			to.setTransferDescription(transferDescription);
			to.setWorkerID2(workerID2);
			to.setFileSize(fileSize);
			
			getOperationsList.add(to);
		}
		
		return getOperationsList;
	}
	
	private List<InitOperationTO> fillInitOperations(Element element) {
		List<InitOperationTO> initOperationsList = new ArrayList<InitOperationTO>();
		
		int opSize = 0;
		
		opSize = element.getElementsByTagName("INIT_OPERATION").getLength();
		
		NamedNodeMap opAttributes = null;
		int jobID;
		int taskID;
		int processID;
		long requestID2;
		String workerID2;
		String localFilePath;
		String remoteFilePath;
		String transferDescription;
		long initTime;
		long endTime;
		long fileSize;
		
		for (int i = 0; i < opSize; i++) {
			
			opAttributes = element.getElementsByTagName("INIT_OPERATION").item(i).getAttributes();

			
			jobID = Integer.valueOf(opAttributes.getNamedItem("jobID").getNodeValue());
			taskID = Integer.valueOf(opAttributes.getNamedItem("taskID").getNodeValue());
			processID = Integer.valueOf(opAttributes.getNamedItem("processID").getNodeValue());
			requestID2 = Long.valueOf(opAttributes.getNamedItem("requestID").getNodeValue());
			workerID2 = opAttributes.getNamedItem("workerID").getNodeValue();
			localFilePath = opAttributes.getNamedItem("localFilePath").getNodeValue();
			remoteFilePath = opAttributes.getNamedItem("remoteFilePath").getNodeValue();
			transferDescription = opAttributes.getNamedItem("transferDescription").getNodeValue();
			initTime = Long.valueOf(opAttributes.getNamedItem("initTime").getNodeValue());
			endTime = Long.valueOf(opAttributes.getNamedItem("endTime").getNodeValue());
			fileSize = Long.valueOf(opAttributes.getNamedItem("fileSize").getNodeValue());
			
			InitOperationTO to = new InitOperationTO();
			to.setEndTime(endTime);
			to.setInitTime(initTime);
			to.setJobID(jobID);
			to.setLocalFilePath(localFilePath);
			to.setProcessID(processID);
			to.setRemoteFilePath(remoteFilePath);
			to.setRequestID2(requestID2);
			to.setTaskID(taskID);
			to.setTransferDescription(transferDescription);
			to.setWorkerID2(workerID2);
			to.setFileSize(fileSize);
			
			initOperationsList.add(to);
		}
		
		return initOperationsList;
	}
}
